#ifndef __MEMORY_POOL_H
#define __MEMORY_POOL_H
#include <iostream>
#include <mutex>
template<size_t BlockSize,size_t BlockNum = 10>
class MemoryPool{
public:
    MemoryPool(){
        std::cout << "create memory pool." << std::endl;
        std::lock_guard<std::mutex> lk(block_mutex);
        free_block_header = nullptr;
        mem_chunk_header = nullptr;
    }
    ~MemoryPool(){
        std::lock_guard<std::mutex> lk(block_mutex);
        while(mem_chunk_header)
        {
            MemChunk* cur_chunk  = mem_chunk_header;
            std::cout << "dealloc memchunk:" << cur_chunk << std::endl;
            mem_chunk_header = mem_chunk_header->next;
            delete cur_chunk;
        }
    }

    void* allocate(){
        std::lock_guard<std::mutex> lk(block_mutex);
        //修改，如果当前的空闲块是空的，说明是第一次申请，则开辟新的chunk块；否则，如果当前块是当前chunk的最后一个块，那么也开辟新的chunk块，将前面的块与新chunk的块链起来
        //如果当申请了批量的块时，方便在释放的时候可以通过链表找到所有的块
        if(!free_block_header || !free_block_header->next)
        {
            MemChunk* new_chunk = new MemChunk();
            for(int i = 1;i < BlockNum;i++)
            {
                new_chunk->blocks[i-1].next = &new_chunk->blocks[i];
            }
            new_chunk->blocks[BlockNum-1].next = nullptr;
            if(BlockNum > 0)
            {
                if(free_block_header)
                {
                    std::cout << "allocate new chunk:"<< new_chunk <<",current block next is new chunk block" << std::endl;
                    free_block_header->next = &new_chunk->blocks[0];//如果是当前chunk的最后一个块，则链接新chunk的块
                }
                else{
                    std::cout << "allocate new chunk:"<< new_chunk <<",current block is new chunk block" << std::endl;
                    free_block_header = &new_chunk->blocks[0];
                }
            }
                
            if(mem_chunk_header)
            {
                mem_chunk_header->next = new_chunk;
                mem_chunk_cur = new_chunk;
            }
            else{
                mem_chunk_header = new_chunk;
                mem_chunk_cur = new_chunk;
            }
        }
        FreeBlock* p = free_block_header;
        free_block_header = free_block_header->next;
        std::cout << "allock block:" << p  << ",current free block header:" << free_block_header << std::endl;
        return p;
    }
    void* allocate(size_t size){
        std::lock_guard<std::mutex> lk(array_block_mutex);

        int n = size / BlockSize;
        void* p = allocate();

        for(int i = 1;i < n;i++){
            allocate();
        }
        std::cout << "allock block,first address:" << p << ",block num:" << n  << std::endl;
        return p; 
        
    }
    void deallocate(void* p){
        std::cout << "deallock block:" << p <<",current free block header:" << free_block_header<< std::endl;
        if(p == nullptr)
            return;
        std::lock_guard<std::mutex> lk(block_mutex);

        FreeBlock* block = static_cast<FreeBlock*>(p);
        block->next = free_block_header;
        free_block_header = block;
    }

    void deallocate(void* p,int n){
        std::cout << "deallock blocks,first address:" << p << ",num:" << n << std::endl;
        if(p == nullptr)
            return;
        std::lock_guard<std::mutex> lk(block_mutex);

        FreeBlock* block = static_cast<FreeBlock*>(p);
        FreeBlock* next_free_block = nullptr;
        for(int i = 0;i < n;i++)
        {
            next_free_block = block->next;//现将要释放的块的下一个地址提前取到
            std::cout << "deallocate block:" << block << ",current free block header:" << free_block_header<< std::endl;
            block->next = free_block_header;//将当前块放到空闲块链表头部，表明可用
            free_block_header = block;
            if(next_free_block)
                block = next_free_block;
            else break;
        }
    }

private:
    struct FreeBlock
    {
        FreeBlock():next(nullptr){}
        unsigned char data[BlockSize];
        FreeBlock* next;
    };
    FreeBlock* free_block_header;
    struct MemChunk{
        MemChunk():next(nullptr){}
        FreeBlock blocks[BlockNum];
        MemChunk* next;
    };
    MemChunk* mem_chunk_header;
    MemChunk* mem_chunk_cur;
    std::mutex block_mutex;
    std::mutex array_block_mutex;
};





#endif